#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cstdlib>
#include <unistd.h>
#include<sys/wait.h>
using namespace std;

//我的系统自定义的环境变量表
const int envnum=64;
char* genv[envnum];

//全局命令行参数表
const int gargnum=64;
char* gargv[gargnum];
int gargc;

//全局当前工作路径
const int basesize=1024;
//有没有PWD一定要区分开
//有PWD=，存入genv
char my_pwd[basesize];
//没有PWD=，得最后路径名
char pwd[basesize];

//上一命令错误返回值
int lastcode=0;

//得到user
string GetUser(){
    string name=getenv("USER");
    return name.empty()?"None":name;
}

//得到hostname
string GetHostName(){
    string hostname=getenv("HOSTNAME");
    return hostname.empty()?"None":hostname;
}

//得到pwd
string GetPwd(){
    //getcwp不依赖环境变量的正确性
    if(getcwd(pwd,sizeof(pwd))==nullptr)
    //为空就不写入genv
        return "None";

    snprintf(my_pwd,basesize,"PWD=%s",pwd);

    //注意在环境变量pwd可能为旧值（像cd后这种情况）要更新
    //my_pwd出作用域之后栈内存会被销毁
    //putenv(my_pwd);
    //setenv自动处理内存管理
    setenv("PWD",pwd,1);//1表示覆盖旧值
    return pwd;
}

//得到最后文件夹名
string LastDir(){
    string curr=GetPwd();
    if(curr=="/"||curr=="None")
        return curr;

    size_t pos=curr.rfind('/');
    if(pos==string::npos)
        return curr;
    return curr.substr(pos+1);

}
    
//打印指令行
void PrintCommand(){
    
    printf("[%s@%s %s]#",
            GetUser().c_str(),GetHostName().c_str(),LastDir().c_str());
    fflush(stdout);
}

//得到命令行：存在局部变量command_buffer[size]
bool GetCommand(char* command_buffer,int size){
    char* result=fgets(command_buffer,size,stdin);
    if(!result)
        return false;

    //把\n换成0
    command_buffer[strlen(command_buffer)-1]=0;
    if(strlen(command_buffer)==0)
        return false;
    return true;
}

//语法解析命令行：分段存入全局的命令行变量表
void ParseCommand(char* command_buffer){
    memset(gargv,0,gargnum);
    gargc=0;
    
    gargv[gargc++]=strtok(command_buffer," ");
    while((bool)(gargv[gargc++]=strtok(nullptr," ")));
    --gargc;
}

//初始化自己的全局环境变量表
void InitEnv(){
    extern char** environ;
    int index=0;
    while(environ[index]&&index<envnum-1){
        //先扩容再拷贝
        genv[index]=(char*)malloc(strlen(environ[index])+1);
        strncpy(genv[index],environ[index],strlen(environ[index])+1);
        index++;
    }
    genv[index]=nullptr;
}
//销毁自己的环境变量表
void DeletEnv(){
    for(int i=0;genv[i];i++){
        free(genv[i]);
    }
}
//添加环境变量
void AddEnv(const char* env){
    int index=0;
    while(genv[index]&&index<envnum-1){
        index++;
    }
    if(index==envnum-1){
        perror("环境变量已满\n");
    }

    genv[index]=(char*)malloc(strlen(env)+1);
    strncpy(genv[index],env,strlen(env)+1);
    genv[++index]=nullptr;
}

void ChDir(const string& dir){
    if(chdir(dir.c_str())!=0){
        perror("chdir failed\n");
    }

    //pwd和my_pwd都要更新
    if(getcwd(pwd,sizeof(pwd))==nullptr){
        perror("getcwd failed");
        return;
    }
    snprintf(my_pwd,sizeof(my_pwd),"PWD=%s",pwd);

    int index=0;
    while(strncmp(genv[index],"PWD=",4)!=0&&index<envnum-1){
        index++;
    }

    if(index==envnum-1){
        perror("环境变量已满\n");
    }
    else if(genv[index]==nullptr){
        genv[index+1]=nullptr;
    }
    else//是之前malloc过的要free掉
        free(genv[index]);

    genv[index]=(char*)malloc(strlen(my_pwd)+1);
    strncpy(genv[index],my_pwd,strlen(my_pwd)+1);
}

//检查并执行内接命令
bool CheckAndBuiltCommand(){
    //cd为内接命令是因为要改变的是当前进程的路径
    if(strcmp(gargv[0],"cd")==0){
        if(gargc==2){
            ChDir(gargv[1]);
        }
        else{
            lastcode=1;
        }
        return true;
    }

    else if(strcmp(gargv[0],"export")==0){
        if(gargc==2){
            AddEnv(gargv[1]);
        }
        else{
            lastcode=2;
        }
            return true;
    }

    else if(strcmp(gargv[0],"env")==0){
        for(int i=0;genv[i];i++){
            printf("%s\n",genv[i]);
        }
        return true;
    }

    else if(strcmp(gargv[0],"echo")==0){
		for(int i=1;i<gargc;i++){
            if(gargv[i][0]=='$'){
               if(gargv[i][1]=='?')
                    printf("%d\n",lastcode);
				else{
					char* var=getenv(gargv[i]+1);
					printf("%s%s",(var?var:" "),(i<gargc-1?" ":""));
				}
             }
             else{
                 printf("%s",gargv[i]);
             }
        }
        return true;

     }
    return false;
}
//外接命令
bool ExecuteCommand(){
    pid_t id=fork();
    if(id<0){
        perror("fork failed!");
        return false;
    }
    else if(id==0){
        execvpe(gargv[0],gargv,genv);
        exit(1);
    }
    else{
        int status=0;
        pid_t chid=waitpid(id,&status,0);
        if(chid<0){
            perror("waitpid failed");
            return false;
        }
        else if(chid>0){
            if(WIFEXITED(status)){
                lastcode=WEXITSTATUS(status);
            }
            else{
                lastcode=100;
            }
            return true;
        }
    }

    return false;
}

int main(){
    char command_buffer[basesize];
    InitEnv();
    while(1){
        PrintCommand();
        if(!GetCommand(command_buffer,basesize))
            continue;

        ParseCommand(command_buffer);
        if(CheckAndBuiltCommand())
            continue;

        ExecuteCommand();

    }
    DeletEnv();
    return 0;
}

